# -*- coding: utf-8 -*-
"""
Created on Sun Mar 28 16:08:25 2021

@author: Apple
"""

import geatpy as ga  # 解遗传算法的库
from pymprog import *  # 解运输问题的库
import numpy as np
import xlrd
import random
import pandas as pd


# -------------------解码函数---------------------------
def decode(Y, A, B, m):
    # step1
    List1 = np.multiply(np.array(Y), np.array(A))  # 计算 y*a

    sum1 = 0 
    for i in List1:
        sum1 += i

    sum2 = 0
    for j in B:
        sum2 += j

    # step2
    while sum1 < sum2:  # 从变量码为0的备选点随机选取一个
        list2 = []
        for i in range(m):
            if Y[i]==0:
                list2.append(i)
        k = np.random.randint(0, len(list2))
        sum1 += A[list2[k]]
        Y[list2[k]] = 1

    return Y
        
        

# -----------------------适值函数-------------------------------
# 固定成本函数
def Fixed_cost(List2):
    fixed_cost = 0  # 再进行累加计算    计算设置物流中心的固定成本
    for i in List2:
        fixed_cost += i
    return fixed_cost


# 运输成本函数
def report(Y_decode, A, B,numa,numb):  # 返回(产地,销地):运价；产地:产量； 销地:销量
    # 把目标函数简化成产大于销的运输问题
    a = []
    b = []
    for i in range(1,numa+1):
        a.append(f'A{i}')
    for i in range(1,numb+1):
        b.append(f'B{i}')
    datas = dict()  # 定义一个空字典 (产地,销地):运价
    datac = dict()  # 定义一个空字典   产地:产量
    datax = dict()  # 定义一个空字典   销地:销量
    List3 = np.multiply(np.array(Y_decode), np.array(A))  # 计算 y*a    产量

    # （产地，销地）：运价
    datas = dict()
    data1 = pd.read_csv('3.csv')
    data2 = pd.read_csv('1.csv')
    Data1 = data1.iloc[:,:].values.tolist()
    Data2 = data2.iloc[:,:].values.tolist()
    for i in range(numa):  # 先从20行里面拿出一行来
        for j in range(numb):  # 再从50列中取一列
            data = Data1[j][i] + Data2[i][2]
            datas[a[i], b[j]] = data  # (产地,销地):运价

    # 产地：产量
    y = List3

    for i in range(numa):
        datac[a[i]] = y[i]  # 产地:产量

    # 销地：销量
    for j in range(numb):
        datax[b[j]] = B[j]  # 销地:销量
    return (datas, datac, datax)


def Transport_cost(datas, datac, datax,numa,numb):
    a = []
    b = []
    for i in range(1,numa+1):
        a.append(f'A{i}')
    for i in range(1,numb+1):
        b.append(f'B{i}')    
    # 开始模型计算
    X = np.zeros((numa, numb))   
    cost = 0.0
    begin('Transport')
    x = var('x', datas.keys())  # 调运方案
    minimize(sum(datas[i, j] * x[i, j] for (i, j) in datas.keys()), 'Cost')  # 总运费最少
    for i in datac.keys():  # 产地产量约束
        sum(x[i, j] for j in datax.keys()) <= datac[i]
    for j in datax.keys():  # 销地销量约束
        sum(x[i, j] for i in datac.keys()) == datax[j]
    solve()

    for (i, j) in datas.keys():
        if x[i, j].primal > 0 and datas[i, j] != 0:         
            X[a.index(i)][b.index(j)] = int(x[i, j].primal)
    cost = int(vobj())
    end()
    return (X, cost)


# -----------------------主函数开始-------------------------------------
def main():
    # 需求点的需求量
    B = [31100,	40000,	30300,	41100,	28400]

    # 物流中心容量
    A = [230000,120000,	480000,	100000,	470000]

    # 各地设置物流中心的固定费用
    D = [40000., 40000., 40000., 40000., 40000.]

    # 从备选物流中心到需求点的单位运输费用
    numa = 5
    numb = 5
    Y = []  # 定义变量码
    Y_decode = []  # 定义解码生成的变量码
    m = 5 # 染色体长度（物流中心个数）
    T = 5  # 迭代次数
    Popsize = 50 # 种群规模
    decode_after_chrom2 = np.zeros((Popsize, m))    # 存储60条Y_decode
    datas = dict()  # 定义一个空字典 (产地,销地):运价
    datac = dict()  # 定义一个空字典   产地:产量
    datax = dict()  # 定义一个空字典   销地:销量
    fixed_cost = 0.0  # 初始固定成本
    X = np.zeros((m, len(B)))  # 运量矩阵
    transport_cost = 0.0  # 初始运输费用
    cost = 0.0  # 初始总费用

    best_y = np.zeros((1, m))  # 所有代中的最优选址方案
    min_t = 100  # 最优的方案出现的代数
    all_min_cost = 999999999999  # 所有代中最小总费用
    all_min_X = np.zeros((m, len(B)))  # 所有代中最小总费用的运量矩阵
    FitnV = np.zeros((1, Popsize))  # 适应度列表
    # NewChrlx = []  # 轮盘赌存储位置
    New_chrom1 = np.zeros((Popsize, m))
    New_chrom2 = np.zeros((Popsize, m))
    Result = np.zeros((T,1))

    # ---------------------遗传编码---------------------------

    # 生成附加码
    FieldDR = np.array([[1, 1, 1, 1, 1],
                        [5, 5, 5, 5, 5],
                        [1, 1, 1, 1, 1]])
    chrom1 = ga.crtpp(Popsize, FieldDR)  # 种群大小，上界下界区域描述器

    # 生成变量码
    chrom2 = ga.crtbp(Popsize, m)
    
    add_code = np.zeros((1, m))  # 定义父代单行附加码
    new_add_code = np.zeros((1, m))  # 定义子代单行附加码
    variable_code = np.zeros((1, m))  # 定义父代单行变量码
    new_variable_code = np.zeros((1, m))  # 定义子代单行变量码
    index_v = 0  # 定义初始索引值
    begin_New_chrom2 = np.zeros((Popsize, m))  # 定义经过交叉后的新的变量码种群
    again_New_chrom1 = np.zeros((Popsize, m))  # 经过变异后的附加码种群
    a = 0
    for t in np.arange(0, T):
        min_cost =  9999999999  # 初始最小总费用
        min_X = np.zeros((m, len(B)))  # 初始最小总费用的运量矩阵
        min_i = 0  # 每一代中最小的运价在种群对应的位置
        better_y = np.zeros((1, m))  # 每一代中的最优选址方案
      # print("==========================第%d代种群======================" % t)

        for i in np.arange(0, Popsize):
            Y = chrom2[i]
            Y_decode = np.array(decode(Y, A, B, m))
            decode_after_chrom2[i] = Y_decode
            List2 = np.multiply(np.array(D), Y_decode)  # 计算 d*y
            fixed_cost = Fixed_cost(List2)
            (datas, datac, datax) = report(Y_decode, A, B,numa,numb)
            (X, transport_cost) = Transport_cost(datas, datac, datax,numa,numb)
            cost = fixed_cost + transport_cost
          # print('第%d个染色体的cost' % i, cost)
            FitnV[0][i] = 1 / cost
            if cost < min_cost:
                min_cost = cost
                min_X = X
                min_i = i
                better_y = Y_decode
            # print("第%d代种群中最小的cost"%t, min_cost)
            # print("最优运输方案", min_X)
            # print('对应的位置', min_i)
        # NewChrlx = ga.rws(np.array(FitnV).T, 59)  # 轮盘赌
        # for i in NewChrlx:
        #     New_chrom1[i] = chrom1[i]
        #     New_chrom2[i] = decode_after_chrom2[i]
        #     for j in range(0, 60):  # 把最大的适值得到染色体拿到新的种群中
        #         if j not in NewChrlx:
        #             New_chrom1[j] = chrom1[j]
        #             New_chrom2[j] = decode_after_chrom2[j]
        
        for i in range(Popsize): # 锦标赛选择方法
            list4 = []
            list3 =  random.sample([i for i in range(Popsize)],10)
            for j in list3:
                list4.append(FitnV[0][j])
            max_index = list4.index(max(list4))
            New_chrom1[i] = chrom1[list3[max_index]]
            New_chrom2[i] = decode_after_chrom2[list3[max_index]]
            
        # 遗传操作
        Pc = 0.6  # 交叉概率
        Pm = 0.1  # 变异概率
        # for i in range(Popsize):
        #     list5 = random.sample([i for i in range(Popsize)],2)
        #     x = list5[0]
        #     y = list5[1]
        #     chrom_rd = ga.crtbp(1, m)
        #     for j in chrom_rd:
        #         if j==1
                    
        New_chrom1 = ga.xovpmx(New_chrom1, Pc)
        
        for change_1 in np.arange(0, Popsize):
            new_add_code = New_chrom1[change_1]
            add_code = chrom1[change_1]
            variable_code = New_chrom2[change_1]
            for change_2 in np.arange(0, m):
                index_v = add_code.tolist().index(new_add_code[change_2])
                new_variable_code[0, change_2] = variable_code[index_v]
            begin_New_chrom2[change_1] = new_variable_code
            new_variable_code = np.zeros((1, m))

        # 变异操作
        ret = np.random.random()
        if ret > Pm:
            for mutation_v in np.arange(0, Popsize):
                new_add_code = New_chrom1[mutation_v]
                random_1 = np.random.randint(0, m)
                random_2 = np.random.randint(0, m)
                a = np.abs(random_1 - random_2)
                tem_list = np.zeros(a + 1)
                if a != 0:
                    if random_2 > random_1:
                        for i, k in zip(np.arange(0, np.abs(random_2 - random_1) + 1), np.arange(random_1, random_2 + 1)):
                            tem_list[i] = new_add_code[k]
                        tem_list = tem_list.tolist()
                        tem_list.reverse()

                        for i, k in zip(np.arange(0, np.abs(random_2 - random_1) + 1).tolist(),
                                        np.arange(random_1, random_2 + 1).tolist()):
                            new_add_code[k] = tem_list[i]

                        again_New_chrom1[mutation_v] = new_add_code

                    else:
                        for i, k in zip(np.arange(0, np.abs(random_2 - random_1) + 1), np.arange(random_2, random_1 + 1)):
                            tem_list[i] = new_add_code[k]

                        tem_list = tem_list.tolist()

                        tem_list.reverse()

                        for i, k in zip(np.arange(0, np.abs(random_2 - random_1) + 1).tolist(),
                                        np.arange(random_2, random_1 + 1).tolist()):
                            new_add_code[k] = tem_list[i]
                        again_New_chrom1[mutation_v] = new_add_code

                else:
                    again_New_chrom1[mutation_v] = new_add_code
                    
        chrom1 = again_New_chrom1
        chrom2 = begin_New_chrom2
        
        print("第%d代种群中最小的cost" % t, min_cost)
        print("最优运输方案\n", min_X)
        print('对应的位置', min_i)
        if all_min_cost > min_cost:
            all_min_cost = min_cost
            all_min_X = min_X
            best_y = better_y
            min_t = t
        Result[t][0] = min_cost

    print("所有代中种群中最小的cost", all_min_cost)
    print("所有代中的最优运输方案\n", all_min_X)
    print("最优的选址方案\n", best_y)
    print("最优的选址方案出现在第%d代" % min_t)
    print(Result)
    with open('Result.txt','a+') as f:  # 输出每代的最优cost
        f.seek(0)
        f.truncate()
        for i in range(0,T):
            f.write(str(i))
            f.write(' ')
            f.write(str(Result[i][0]))
            f.write('\n')
        f.close()
    with open('Scheduling scheme.txt','a+') as f: # 输出最优运输方案
        f.seek(0)
        f.truncate()
        for i in range(len(min_X)):
            for j in range(len(min_X[i])):
                f.write(str(min_X[i][j]))
                f.write(' ')
            f.write('\n')
        f.close()
        

if __name__ == "__main__":
    main()

